import tkinter as tk
import random
from tkinter import messagebox, simpledialog
from constants import Player, BoardConfig, PATTERN_SCORES, Difficulty, SCORE_WEIGHTS, PatternConstants

class GobangGame:
    def __init__(self, root):
        self.root = root
        self.root.title("五子棋应用")
        self.root.geometry("800x600+100+100")
        self.root.resizable(True, True)

        # 游戏参数
        self.board_size = BoardConfig.SIZE  # 棋盘大小
        self.cell_size = BoardConfig.CELL_SIZE   # 每个格子大小
        self.margin = BoardConfig.MARGIN      # 边距
        self.current_player = Player.BLACK  # 初始化为枚举类型
        self.board = [[None for _ in range(self.board_size)] for _ in range(self.board_size)]
        self.player_color = Player.BLACK  # 默认玩家执黑棋
        self.difficulty = Difficulty.MEDIUM  # 默认中等难度
        self.ai_opponent = True  # 开启电脑对战

        # 创建菜单栏
        self.create_menu()

        # 创建棋盘画布
        self.canvas = tk.Canvas(root, width=self.board_size*self.cell_size+self.margin*2,
                               height=self.board_size*self.cell_size+self.margin*2,
                               bg='#E8C090')
        self.canvas.pack(pady=20)

        # 绑定鼠标点击事件
        self.canvas.bind('<Button-1>', self.on_click)

        # 绘制初始棋盘
        self.draw_board()

    def create_menu(self):
        # 创建菜单栏
        menubar = tk.Menu(self.root)

        # 创建游戏菜单
        game_menu = tk.Menu(menubar, tearoff=0)
        game_menu.add_command(label="开始新游戏", command=self.start_new_game)
        game_menu.add_separator()
        game_menu.add_command(label="退出", command=self.root.quit)
        menubar.add_cascade(label="游戏", menu=game_menu)

        # 创建游戏记录菜单
        record_menu = tk.Menu(menubar, tearoff=0)
        record_menu.add_command(label="查看记录", command=self.view_record)
        record_menu.add_command(label="清空记录", command=self.clear_record)
        menubar.add_cascade(label="游戏记录", menu=record_menu)

        # 创建游戏设置菜单
        setting_menu = tk.Menu(menubar, tearoff=0)
        setting_menu.add_command(label="选择棋子颜色", command=self.choose_player_color)
        setting_menu.add_command(label="棋盘大小", command=self.set_board_size)
        setting_menu.add_command(label="难度设置", command=self.set_difficulty)
        menubar.add_cascade(label="游戏设置", menu=setting_menu)

        # 设置菜单栏
        self.root.config(menu=menubar)

    def draw_board(self):
        # 绘制棋盘网格
        for i in range(self.board_size):
            # 横线
            self.canvas.create_line(self.margin, self.margin + i*self.cell_size,
                                   self.margin + (self.board_size-1)*self.cell_size, self.margin + i*self.cell_size,
                                   width=2)
            # 竖线
            self.canvas.create_line(self.margin + i*self.cell_size, self.margin,
                                   self.margin + i*self.cell_size, self.margin + (self.board_size-1)*self.cell_size,
                                   width=2)

        # 绘制棋盘上的五个星位点
        star_points = [(3,3), (3,11), (7,7), (11,3), (11,11)]
        for (x, y) in star_points:
            self.canvas.create_oval(self.margin + x*self.cell_size - 4, self.margin + y*self.cell_size - 4,
                                   self.margin + x*self.cell_size + 4, self.margin + y*self.cell_size + 4,
                                   fill='black')

    def on_click(self, event):
        # 如果是电脑回合，则不响应点击
        if self.ai_opponent and self.current_player != self.player_color:
            return

        # 计算点击位置对应的棋盘坐标
        x = (event.x - self.margin) // self.cell_size
        y = (event.y - self.margin) // self.cell_size
        # 确保坐标在有效范围内
        x = max(0, min(x, self.board_size - 1))
        y = max(0, min(y, self.board_size - 1))

        # 检查坐标是否有效且该位置为空
        if 0 <= x < self.board_size and 0 <= y < self.board_size and not self.board[x][y]:
            self.place_stone(x, y)
            if self.check_win(x, y):
                messagebox.showinfo("游戏结束", f"您({self.current_player.value})获胜！")
                self.start_new_game()
            else:
                # 切换到电脑回合
                self.current_player = Player.WHITE if self.current_player == Player.BLACK else Player.BLACK
                if self.ai_opponent:
                    self.root.after(500, self.ai_make_move)  # 延迟500毫秒让电脑落子

    def place_stone(self, x, y):
        # 记录棋子位置
        self.board[x][y] = self.current_player

        # 绘制棋子
        center_x = self.margin + x * self.cell_size
        center_y = self.margin + y * self.cell_size
        radius = self.cell_size // 2 - 2

        self.canvas.create_oval(center_x - radius, center_y - radius,
                               center_x + radius, center_y + radius,
                               fill=self.current_player.value)

    def check_win(self, x, y):
        # 检查横向、纵向、对角线是否有五子连珠
        directions = [(1,0), (0,1), (1,1), (1,-1)]
        stone_type = self.board[x][y] if self.board[x][y] else None

        for dx, dy in directions:
            count = 1
            # 正向检查
            for i in range(1, 5):
                nx, ny = x + dx*i, y + dy*i
                if 0 <= nx < self.board_size and 0 <= ny < self.board_size and self.board[nx][ny] == stone_type:
                    count += 1
                else:
                    break
            # 反向检查
            for i in range(1, 5):
                nx, ny = x - dx*i, y - dy*i
                if 0 <= nx < self.board_size and 0 <= ny < self.board_size and self.board[nx][ny] == stone_type:
                    count += 1
                else:
                    break
            if count >= 5:
                return True
        return False

    def start_new_game(self):
        # 重置游戏状态
        self.canvas.delete('all')
        self.board = [[None for _ in range(self.board_size)] for _ in range(self.board_size)]
        # 根据玩家选择的颜色决定谁先行
        self.current_player = Player.BLACK if self.player_color == Player.WHITE else self.player_color
        self.draw_board()
        messagebox.showinfo("新游戏", f"开始新的一局游戏！{self.current_player.value}先行")

        # 如果是电脑先行（玩家选择白棋），则自动让电脑落子
        if self.ai_opponent and self.current_player != self.player_color:
            self.root.after(500, self.ai_make_move)

    def ai_make_move(self):
        # 电脑AI落子逻辑（优化策略：基于棋型评分系统）
        best_move = self.find_best_move()
        if best_move:
            x, y = best_move
            self.place_stone(x, y)
            if self.check_win(x, y):
                messagebox.showinfo("游戏结束", f"电脑({self.current_player.value})获胜！")
                self.start_new_game()
            else:
                # 切换到玩家回合
                self.current_player = Player.WHITE if self.current_player == Player.BLACK else Player.BLACK

    def find_best_move(self):
        # 基于评分系统和获胜步数计算寻找最佳落子位置
        best_score = -1
        best_move = None
        empty_positions = []
        opponent = Player.WHITE if self.current_player == Player.BLACK else Player.BLACK

        # 多层次防守逻辑：先挡必杀，再挡威胁
        player_win_move = self.find_winning_move(opponent)
        if player_win_move:
            return player_win_move

        # 阻挡玩家高威胁棋型（活四、冲四、活三）
        player_threats = self.find_high_threats(opponent)
        if player_threats:
            return max(player_threats, key=lambda x: x[1])[0]

        # 收集所有空位并评分
        for x in range(self.board_size):
            for y in range(self.board_size):
                if not self.board[x][y]:
                    # 计算当前玩家和对手在此位置落子后的获胜步数
                    ai_score, ai_steps = self.evaluate_position(x, y, self.current_player)
                    player_score, player_steps = self.evaluate_position(x, y, opponent)

                    # 核心防守逻辑：比较双方获胜步数
                    steps_diff = player_steps - ai_steps
                    defense_urgency = 0
                    attack_priority = 0

                    # 如果玩家能更快获胜（步数更少），增加防守紧迫性
                    if player_steps < ai_steps:
                        # 步数差越大，防守优先级越高
                        defense_urgency = (ai_steps - player_steps) * 2000
                    # 如果AI能更快获胜，增加进攻优先级
                    elif ai_steps < player_steps:
                        attack_priority = (player_steps - ai_steps) * 1500

                    # 综合评分公式：防守紧迫性 > 进攻优先级 > 棋型评分
                    # 增强防守优先级，调整评分权重
                    # 平衡攻防优先级，略微提高进攻权重
                    total_score = defense_urgency * SCORE_WEIGHTS['defense'] + attack_priority * SCORE_WEIGHTS['attack'] + ai_score * SCORE_WEIGHTS['ai_score'] + player_score * SCORE_WEIGHTS['player_score']

                     # 开局多样化策略
                    if self.is_early_game():
                         # 中心区域奖励 + 开局多样化权重
                         total_score += self.calculate_center_bonus(x, y)
                         # 为边角位置添加少量随机性奖励，避免开局固定
                         if (x == 0 or x == self.board_size-1 or y == 0 or y == self.board_size-1):
                             total_score += random.randint(5, 15)

                     # 记录最高分位置
                    if total_score > best_score:
                        best_score = total_score
                        best_move = (x, y)
                        empty_positions = [(x, y)]
                    elif total_score == best_score:
                        empty_positions.append((x, y))

        # 增加随机性，避免开局固定
        if self.is_early_game() and len(empty_positions) > 3:
            # 开局阶段从高分位置中随机选择3个候选位置
            return random.choice(empty_positions[:3])
        # 根据难度调整随机性
        if self.difficulty == Difficulty.EASY and len(empty_positions) > 5:
            return random.choice(empty_positions[:5])
        elif self.difficulty == Difficulty.MEDIUM and len(empty_positions) > 3:
            return random.choice(empty_positions[:3])
        return empty_positions[0] if empty_positions else None

    def get_line_in_direction(self, x, y, dx, dy, player):
        # 获取指定方向上的连续棋子和空位
        line = []
        # 添加当前位置
        line.append(PatternConstants.PLAYER)
        
        # 向正方向查找
        cx, cy = x + dx, y + dy
        while True:
            if cx < 0 or cx >= self.board_size or cy < 0 or cy >= self.board_size:
                line.append(PatternConstants.EDGE)
                break
            cell = self.board[cx][cy]
            if cell is None:
                line.append(PatternConstants.EMPTY)
                break
            elif cell == player:
                line.append(PatternConstants.PLAYER)
            else:
                line.append(PatternConstants.OPPONENT)
                break
            cx += dx
            cy += dy
        
        # 向反方向查找
        cx, cy = x - dx, y - dy
        while True:
            if cx < 0 or cx >= self.board_size or cy < 0 or cy >= self.board_size:
                line.insert(0, PatternConstants.EDGE)
                break
            cell = self.board[cx][cy]
            if cell is None:
                line.insert(0, PatternConstants.EMPTY)
                break
            elif cell == player:
                line.insert(0, PatternConstants.PLAYER)
            else:
                line.insert(0, PatternConstants.OPPONENT)
                break
            cx -= dx
            cy -= dy
        
        return line

    def evaluate_position(self, x, y, player):
        # 评估位置价值，返回(分数, 最小获胜步数)
        if not (0 <= x < self.board_size and 0 <= y < self.board_size):
            return (0, float('inf'))

        # 临时落子
        self.board[x][y] = player
        total_score = 0
        min_steps_to_win = float('inf')  # 最小获胜步数
        directions = [(1,0), (0,1), (1,1), (1,-1)]

        for dx, dy in directions:
            line = self.get_line_in_direction(x, y, dx, dy, player)
            score, steps = self.evaluate_line(line)
            total_score += score
            if steps < min_steps_to_win:
                min_steps_to_win = steps

        # 恢复空位
        self.board[x][y] = None
        return (total_score, min_steps_to_win if min_steps_to_win != float('inf') else 10)  # 默认10步

    def evaluate_line(self, line):
        # 根据棋型评估分数和获胜步数
        patterns = PATTERN_SCORES  # 从constants导入棋型评分配置
        

        max_score = 0
        min_steps = float('inf')
        for pattern, score, steps in patterns:
            pattern_len = len(pattern)
            for i in range(len(line) - pattern_len + 1):
                window = line[i:i+pattern_len]
                if self.match_pattern(window, pattern):
                    if score > max_score or (score == max_score and steps < min_steps):
                        max_score = score
                        min_steps = steps
        return (max_score, min_steps if min_steps != float('inf') else 10)

    def match_pattern(self, window, pattern):
        # 检查窗口是否匹配棋型
        for w, p in zip(window, pattern):
            if p == PatternConstants.PLAYER and w != PatternConstants.PLAYER:
                return False
            if p == PatternConstants.OPPONENT and w != PatternConstants.OPPONENT:
                return False
            if p == PatternConstants.EMPTY and w not in [PatternConstants.EMPTY, PatternConstants.EDGE]:
                return False
            if p == PatternConstants.EDGE and w != PatternConstants.EDGE:
                return False
        return True

    def is_early_game(self):
        # 判断是否为游戏初期（前10步）
        stones_count = sum(row.count(Player.BLACK) + row.count(Player.WHITE) for row in self.board)
        return stones_count < 10

    def calculate_center_bonus(self, x, y):
        # 改进的中心区域奖励，增加开局多样性
        center = self.board_size // 2
        distance = abs(x - center) + abs(y - center)
        base_bonus = max(0, 50 - distance * 5)
        
        # 开局阶段为非中心区域添加随机奖励，增加多样性
        if self.is_early_game() and distance > 3:
            base_bonus += random.randint(10, 30)
        return base_bonus

    def find_winning_move(self, player):
        # 检查指定玩家是否有一步获胜棋
        for x in range(self.board_size):
            for y in range(self.board_size):
                if not self.board[x][y]:
                    self.board[x][y] = player
                    if self.check_win(x, y):
                        self.board[x][y] = None
                        return (x, y)
                    self.board[x][y] = None
        return None

    def find_high_threats(self, player):
        # 寻找玩家的高威胁棋型（活四、冲四、活三）
        threats = []
        for x in range(self.board_size):
            for y in range(self.board_size):
                if not self.board[x][y]:
                    score, _ = self.evaluate_position(x, y, player)
                    # 活四、冲四、活三级别威胁
                    if score >= 1000:
                        threats.append(((x, y), score))
        return threats

    # 移除旧的AI方法
    # def find_blocking_move(self):
    # def find_random_move(self):

    def choose_player_color(self):
        # 让玩家选择棋子颜色
        color = simpledialog.askstring("选择棋子颜色", "请选择您要使用的棋子颜色 (black/white):",
                                      initialvalue=self.player_color.value)
        if color and color.lower() in [Player.BLACK.value, Player.WHITE.value]:
            self.player_color = Player[color.upper()]
            messagebox.showinfo("设置成功", f"您已选择执{self.player_color.value}棋\n新游戏将应用此设置")

    def view_record(self):
        messagebox.showinfo("游戏记录", "功能开发中：查看游戏记录")

    def clear_record(self):
        messagebox.showinfo("游戏记录", "功能开发中：清空游戏记录")

    def set_board_size(self):
        messagebox.showinfo("棋盘大小", "功能开发中：设置棋盘大小")

    def set_difficulty(self):
        difficulty = simpledialog.askstring("选择难度", "请选择难度 (easy/medium/hard):", initialvalue=self.difficulty.value)
        if difficulty and difficulty.lower() in [Difficulty.EASY.value, Difficulty.MEDIUM.value, Difficulty.HARD.value]:
            self.difficulty = Difficulty[difficulty.upper()]
            messagebox.showinfo("设置成功", f"难度已设为{self.difficulty.value}")

if __name__ == "__main__":
    root = tk.Tk()
    game = GobangGame(root)
    root.mainloop()